﻿using Sidvall.Serialization;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;

namespace Sidvall.Security
{
	public class ClaimsPrincipalManager : IClaimsPrincipalManager
	{
		#region Public Members

		#region AddClaim

		public void AddClaim(IIdentity identity, Claim claim)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			System.Security.Claims.Claim item;
			IClaimProvider claimProvider;

			if (identity == null)
				return;
			if (claim == null)
				return;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				item = Convert(claim);
				claimsIdentity.AddClaim(item);
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					claimProvider.AddClaim(claim);
			}
		}

		#endregion
		#region AddClaims

		public void AddClaims(IIdentity identity, IEnumerable<Claim> claims)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			System.Security.Claims.Claim item;
			IClaimProvider claimProvider;

			if (identity == null)
				return;
			if (claims == null)
				return;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				foreach (var claim in claims)
				{
					item = Convert(claim);
					claimsIdentity.AddClaim(item);
				}
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					claimProvider.AddClaims(claims);
			}
		}

		#endregion
		#region ClearClaims

		public void ClearClaims(IIdentity identity)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			IClaimProvider claimProvider;

			if (identity == null)
				return;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				var items = claimsIdentity.Claims.ToList();
				foreach (var item in items)
					claimsIdentity.TryRemoveClaim(item);
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					claimProvider.ClearClaims();
			}
		}

		#endregion
		#region FindClaim

		public Claim FindClaim(IIdentity identity, string claimType)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			System.Security.Claims.Claim claim;
			IClaimProvider claimProvider;

			if (identity == null)
				return null;
			if (string.IsNullOrWhiteSpace(claimType))
				return null;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				if (!string.IsNullOrWhiteSpace(claimType))
					claim = claimsIdentity.FindFirst(claimType);
				else
					claim = claimsIdentity.Claims.FirstOrDefault();
				if (claim == null)
					return null;
				return Convert(claim);
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					return claimProvider.FindClaim(claimType);
			}
			return null;
		}

		#endregion
		#region FindClaims

		public ClaimCollection FindClaims(IIdentity identity, string claimType)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			IEnumerable<System.Security.Claims.Claim> claims;
			ClaimCollection items;
			IClaimProvider claimProvider;

			if (identity == null)
				return null;
			items = new ClaimCollection();
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				if (!string.IsNullOrWhiteSpace(claimType))
					claims = claimsIdentity.FindAll(claimType);
				else
					claims = claimsIdentity.Claims;
				foreach (var claim in claims)
					items.Add(claim.Type, claim.Value, claim.ValueType);
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					return claimProvider.FindClaims(claimType);
			}
			return items;
		}

		#endregion
		#region HasClaim

		public bool HasClaim(IIdentity identity, string claimType, string value)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			IClaimProvider claimProvider;

			if (identity == null)
				return false;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				return claimsIdentity.HasClaim(claimType, value);
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					return claimProvider.HasClaim(claimType, value);
			}
			return false;
		}

		#endregion
		#region RemoveClaim

		public bool RemoveClaim(IIdentity identity, Claim claim)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			System.Security.Claims.Claim item;
			IClaimProvider claimProvider;

			if (identity == null)
				return false;
			if (claim == null)
				return false;
			if (string.IsNullOrWhiteSpace(claim.ClaimType))
				return false;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				item = claimsIdentity.FindFirst(claim.ClaimType);
				if ((item != null) && (item.Value.Equals(claim.Value, true, System.StringComparison.OrdinalIgnoreCase)))
					return claimsIdentity.TryRemoveClaim(item);
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					return claimProvider.RemoveClaim(claim);
			}
			return false;
		}

		#endregion
		#region RemoveClaims

		public void RemoveClaims(IIdentity identity, IEnumerable<Claim> claims)
		{
			System.Security.Claims.ClaimsIdentity claimsIdentity;
			System.Security.Claims.Claim item;
			IClaimProvider claimProvider;

			if (identity == null)
				return;
			if (claims == null)
				return;
			claimsIdentity = identity as System.Security.Claims.ClaimsIdentity;
			if (claimsIdentity != null)
			{
				foreach (var claim in claims)
				{
					item = claimsIdentity.FindFirst(claim.ClaimType);
					if ((item != null) && (item.Value.Equals(claim.Value, true, System.StringComparison.OrdinalIgnoreCase)))
						claimsIdentity.TryRemoveClaim(item);
				}
			}
			else
			{
				claimProvider = identity as IClaimProvider;
				if (claimProvider != null)
					claimProvider.RemoveClaims(claims);
			}
		}

		#endregion
		#region ToString

		public override string ToString()
		{
			return "ClaimManager - ClaimsIdentity and IClaimProvider";
		}

		#endregion

		#endregion
		#region Private Members

		#region Convert

		private static System.Security.Claims.Claim Convert(Claim item)
		{
			if (item == null)
				return null;
			return new System.Security.Claims.Claim(item.ClaimType, item.Value, item.ValueType);
		}
		private static Claim Convert(System.Security.Claims.Claim item)
		{
			if (item == null)
				return null;
			return new Claim()
			{
				ClaimType = item.Type,
				Value = item.Value,
				ValueType = item.ValueType,
			};
		}

		#endregion

		#endregion
	}
}
